#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QFile>
#include <QDebug>
#include <QFileDialog>
#include <QTextCodec>
#include <QSettings>
#include "selectlangtypedialog.h"

MainWidget::MainWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWidget),
    mThread(new CodeCounterThread(this))
{
    ui->setupUi(this);
    ui->stop->setEnabled(false);
    ui->outputResult->setEnabled(false);
    ui->langTypes->setText("c/c++");

    initWidgets();
    loadSourceDirHistory();
    loadLangTypesConfig();

    mThread->start();

    connect(mThread->worker(), SIGNAL(started()), this, SLOT(onCountStarted()));
    connect(mThread->worker(), SIGNAL(stopped()), this, SLOT(onCountStopped()));
    connect(mThread->worker(), SIGNAL(fileFinished(FileCountResult,CountResult)),
            this, SLOT(onCountFileFinished(FileCountResult,CountResult)));
    connect(mThread->worker(), SIGNAL(finished(CountResult,bool)),
            this, SLOT(onCountFinished(CountResult,bool)));
}

MainWidget::~MainWidget()
{
    saveSourceDirHistory();
    delete ui;
}

void MainWidget::on_selectLangTypes_clicked()
{
    SelectLangTypeDialog dlg(this);
    dlg.setSelectedLangTypes(ui->langTypes->text().split(','));
    dlg.setAllLangTypes(mLangTypeNames, mLangTypes);
    if (dlg.exec() != QDialog::Accepted)
        return;

    ui->langTypes->setText(dlg.getSelectedLangTypes().join(','));
    dlg.getAllLangTypes(mLangTypeNames, mLangTypes);

    saveLangTypesConfig();
}

void MainWidget::on_selectSourceDir_clicked()
{
    QString path = QFileDialog::getExistingDirectory(this, "选择源码目录");

    if (!path.isEmpty())
        ui->sourceDir->setEditText(path);
}

void MainWidget::on_start_clicked()
{
    QString sourceDir = ui->sourceDir->currentText();
    if (sourceDir.isEmpty() || !QDir(sourceDir).exists())
    {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("请选择有效的源码目录！");
        msgBox.exec();
        return;
    }

    mSourceDir = sourceDir;
    if (ui->sourceDir->findText(mSourceDir) == -1)
    {
        ui->sourceDir->insertItem(0, mSourceDir);
        if (ui->sourceDir->count() == 1)
            ui->sourceDir->addItem("<清空历史记录>", "clear");
    }

    QStringList langTypeNames = ui->langTypes->text().split(',');
    QList<LangType> langTypes;
    foreach (const QString& name, langTypeNames)
    {
        auto pos = mLangTypes.find(name);
        if (pos != mLangTypes.end())
            langTypes.append(pos.value());
    }

    mThread->setLangTypes(langTypes);
    mThread->setSourceDirectory(sourceDir);
    mThread->setContainsSubDir(ui->containsSubDir->isChecked());

    ui->tableWidget->setRowCount(0);
    ui->totalLines->clear();
    ui->codeLines->clear();
    ui->commentLines->clear();
    ui->blankLines->clear();

    QMetaObject::invokeMethod(mThread->worker(), "startCount", Qt::QueuedConnection);
}

void MainWidget::on_stop_clicked()
{
    QMetaObject::invokeMethod(mThread->worker(), "stopCount", Qt::QueuedConnection);
}

void MainWidget::on_outputResult_clicked()
{
    if (ui->totalLines->text().isEmpty())
    {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("没有要输出的内容，请先执行统计！");
        msgBox.exec();
        return;
    }

    QString fileName = QFileDialog::getSaveFileName(
                this, "保存到文件", "./output.txt", "Text (*.txt)");
    if (fileName.isEmpty())
        return;

    QList<int> columnWidth;
    for (int i=0; i<ui->tableWidget->columnCount(); ++i)
        columnWidth << getTableColumnWidth(i);

    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "open file failed" << fileName;
        return;
    }

    QTextStream stream(&file);
    stream.setCodec("utf-8");
    stream << QString("源码目录：") << mSourceDir << "\n\n";

    for (int i=0; i<ui->tableWidget->columnCount(); ++i)
        outputHeaderLabel(stream, i, columnWidth[i]);

    stream << '\n';

    int rowCount = ui->tableWidget->rowCount();
    for (int i=0; i<rowCount; ++i)
    {
        for (int j=0; j<ui->tableWidget->columnCount(); ++j)
            outputTableItem(stream, i, j, columnWidth[j]);

        stream << '\n';
    }

    stream << '\n';
    stream << QString("代码行数：\t") << ui->codeLines->text() << "\n";
    stream << QString("注释行数：\t") << ui->commentLines->text() << "\n";
    stream << QString("空白行数：\t") << ui->blankLines->text() << "\n";
    stream << QString("总行数：\t") << ui->totalLines->text() << "\n";

    QMessageBox msgBox(this);
    msgBox.setWindowTitle("提示");
    msgBox.setText("已保存！");
    msgBox.exec();
}

void MainWidget::on_about_clicked()
{
    QMessageBox::aboutQt(this, "关于Qt");
}

void MainWidget::on_quit_clicked()
{
   close();
}

void MainWidget::onCountStarted()
{
    ui->start->setEnabled(false);
    ui->stop->setEnabled(true);
    ui->outputResult->setEnabled(false);
}

void MainWidget::onCountStopped()
{
    ui->start->setEnabled(true);
    ui->stop->setEnabled(false);
    ui->outputResult->setEnabled(true);
}

void MainWidget::onCountFileFinished(
        const FileCountResult& fileResult, const CountResult& result)
{
    QString path = "./" + fileResult.path;
    if (!path.endsWith('/'))
        path += '/';

    int row = ui->tableWidget->rowCount();
    ui->tableWidget->insertRow(row);
    ui->tableWidget->setItem(row, 0, new QTableWidgetItem(fileResult.fileName));
    ui->tableWidget->setItem(row, 1, new QTableWidgetItem(path));
    ui->tableWidget->setItem(
                row, 2, new QTableWidgetItem(
                    QString::number(fileResult.countInfo.codeLines)));
    ui->tableWidget->setItem(
                row, 3, new QTableWidgetItem(
                    QString::number(fileResult.countInfo.commentLines)));
    ui->tableWidget->setItem(
                row, 4, new QTableWidgetItem(
                    QString::number(fileResult.countInfo.blankLines)));
    ui->tableWidget->setItem(
                row, 5, new QTableWidgetItem(
                    QString::number(fileResult.countInfo.totalLines)));

    ui->codeLines->setText(QString::number(result.codeLines));
    ui->blankLines->setText(QString::number(result.blankLines));
    ui->commentLines->setText(QString::number(result.commentLines));
    ui->totalLines->setText(QString::number(result.totalLines));
}

void MainWidget::onCountFinished(const CountResult& result, bool userStop)
{
    ui->codeLines->setText(QString::number(result.codeLines));
    ui->blankLines->setText(QString::number(result.blankLines));
    ui->commentLines->setText(QString::number(result.commentLines));
    ui->totalLines->setText(QString::number(result.totalLines));

    ui->start->setEnabled(true);
    ui->stop->setEnabled(false);
    ui->outputResult->setEnabled(true);

    if (!userStop)
    {
        QMessageBox msgBox(this);
        msgBox.setWindowTitle("提示");
        msgBox.setText("统计完成！");
        msgBox.exec();
    }
}

void MainWidget::closeEvent(QCloseEvent* event)
{
    on_stop_clicked();
    mThread->quit();
    mThread->wait();

    QWidget::closeEvent(event);
}

void MainWidget::initWidgets()
{
    QStringList headerLabels;
    headerLabels << "文件名" << "路径" << "代码行数"
                 << "注释行数" << "空白行数" << "总行数";
    ui->tableWidget->setColumnCount(headerLabels.size());
    ui->tableWidget->setHorizontalHeaderLabels(headerLabels);
    QHeaderView* headerView = ui->tableWidget->horizontalHeader();
    headerView->resizeSection(0, 150);
    headerView->resizeSection(1, 150);
}

void MainWidget::loadSourceDirHistory()
{
    QSettings history("zhouyuanchao.com", "CodeCounter");
    QStringList paths = history.value("SourceDirHistory")
            .toString().split(',', QString::SkipEmptyParts);

    if (paths.isEmpty())
        return;

    ui->sourceDir->addItems(paths);
    ui->sourceDir->addItem("<清空历史记录>", "clear");
    ui->sourceDir->setCurrentText("");
}

void MainWidget::saveSourceDirHistory()
{
    QStringList paths;

    if (ui->sourceDir->count() != 0)
    {
        for (int i=0; i<ui->sourceDir->count() - 1; ++i)
            paths.append(ui->sourceDir->itemText(i));
    }

    QSettings history("zhouyuanchao.com", "CodeCounter");
    history.setValue("SourceDirHistory", paths.join(','));
}

void MainWidget::loadLangTypesConfig()
{
    Q_ASSERT(mLangTypeNames.isEmpty());
    Q_ASSERT(mLangTypes.isEmpty());

    QFile file("lang_config.json");
    if (!file.open(QIODevice::Text | QIODevice::ReadOnly))
        return;

    QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll());
    QJsonArray jsonArray = jsonDoc.array();
    for (int i=0; i<jsonArray.size(); ++i)
    {
        LangType langType;

        QJsonObject langTypeObj = jsonArray[i].toObject();

        langType.name = langTypeObj.value("name").toString();
        if (langType.name.isEmpty())
            continue;

        QJsonArray extsArray = langTypeObj.value("exts").toArray();
        for (int j=0; j<extsArray.size(); ++j)
            langType.fileExtensions.append(extsArray[j].toString());

        langType.singleLineComment =
                langTypeObj.value("single_line_comment").toString();
        langType.multiLineCommentsBegin =
                langTypeObj.value("multi_line_comment_begin").toString();
        langType.multiLineCommentsEnd =
                langTypeObj.value("multi_line_comment_end").toString();

        mLangTypeNames.append(langType.name);
        mLangTypes.insert(langType.name, langType);
    }
}

void MainWidget::saveLangTypesConfig()
{
    QFile file("lang_config.json");
    if (!file.open(QIODevice::Text | QIODevice::WriteOnly))
        return;

    QJsonArray jsonArray;
    for (int i=0; i<mLangTypeNames.size(); ++i)
    {
        QJsonObject langTypeObj;
        const LangType& langType = mLangTypes[mLangTypeNames[i]];
        langTypeObj["name"] = QJsonValue(langType.name);
        langTypeObj["single_line_comment"] = QJsonValue(langType.singleLineComment);
        langTypeObj["multi_line_comment_begin"] = QJsonValue(langType.multiLineCommentsBegin);
        langTypeObj["multi_line_comment_end"] = QJsonValue(langType.multiLineCommentsEnd);

        QJsonArray extsArray;
        for (int i=0; i<langType.fileExtensions.size(); ++i)
            extsArray.append(QJsonValue(langType.fileExtensions[i]));

        langTypeObj["exts"] = QJsonValue(extsArray);

        jsonArray.append(QJsonValue(langTypeObj));
    }

    QJsonDocument jsonDoc;
    jsonDoc.setArray(jsonArray);

    file.write(jsonDoc.toJson());
}

int MainWidget::getTableColumnWidth(int column)
{
    int width = ui->tableWidget->horizontalHeaderItem(column)->text().size() * 2;

    for (int i=0; i<ui->tableWidget->rowCount(); ++i)
        width = qMax(width, ui->tableWidget->item(i, column)->text().size());

    return width;
}

void MainWidget::outputHeaderLabel(QTextStream& stream, int column, int width)
{
    QString text = ui->tableWidget->horizontalHeaderItem(column)->text();
    stream << text;

    int spaceCount = width - text.size() * 2;
    for (int i=0; i<spaceCount; ++i)
        stream << ' ';

    if (column != ui->tableWidget->columnCount() - 1)
        stream << '\t';
}

void MainWidget::outputTableItem(
        QTextStream& stream, int row, int column, int width)
{
    QString text = ui->tableWidget->item(row, column)->text();
    stream << text;

    int spaceCount = width - text.size();
    for (int i=0; i<spaceCount; ++i)
        stream << ' ';

    if (column != ui->tableWidget->columnCount() - 1)
        stream << '\t';
}

void MainWidget::on_sourceDir_currentIndexChanged(int index)
{
    if (index == -1)
        return;

    if (ui->sourceDir->itemData(index).toString() == "clear")
        ui->sourceDir->clear();
}
